package com.jiubanqingchen.wish.framework.base.service;

import cn.hutool.core.lang.Editor;
import cn.hutool.core.util.ArrayUtil;
import com.alibaba.excel.write.style.row.SimpleRowHeightStyleStrategy;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.jfinal.kit.Kv;
import com.jfinal.kit.StrKit;
import com.jfinal.plugin.activerecord.*;
import com.jiubanqingchen.wish.framework.annotation.UniqueColumn;
import com.jiubanqingchen.wish.framework.base.user.IUser;
import com.jiubanqingchen.wish.framework.exception.WishException;
import com.jiubanqingchen.wish.framework.exception.WishExceptionKey;
import com.jiubanqingchen.wish.kit.AopKit;
import com.jiubanqingchen.wish.kit.BlankKit;
import com.jiubanqingchen.wish.kit.ModelKit;
import com.jiubanqingchen.wish.kit.SqlKit;
import com.jiubanqingchen.wish.plugin.easyexcel.EasyExcelSupport;
import com.jiubanqingchen.wish.plugin.easyexcel.style.column.CustomWidthStyleStrategy;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.servlet.http.HttpServletResponse;
import java.lang.reflect.ParameterizedType;
import java.util.Date;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;

/**
 * model service 相关实现
 *
 * @author light_dust
 * @since 2020/12/22 14:42
 */
@SuppressWarnings({"rawtypes", "unchecked"})
public class ModelService<M extends Model<M>> extends BaseService implements IModelService<M>, IFileService {

    private static final Logger logger = LoggerFactory.getLogger(ModelService.class);

    private Class<M> modelClazz;

    private Model<M> modelDao;

    protected Class<M> getModelClazz() {

        if (modelClazz == null) {
            //由于使用了aop.get() 注入 对象被cglib增强过 所有需要获取原始类
            modelClazz = (Class<M>) ((ParameterizedType) getOriginalClass().getGenericSuperclass()).getActualTypeArguments()[0];
        }
        return modelClazz;
    }

    /**
     * 获取原始对象
     */
    private Class<?> getOriginalClass() {
        return AopKit.me().getUsefulClass(getClass());
    }

    protected Model<M> getModelDao() throws WishException {
        if (modelDao == null) {
            try {
                modelDao = getModelClazz().newInstance().dao();
            } catch (Exception e) {
                throw new WishException(WishExceptionKey.MODEL_CREATE_DAO_ERROR, e, getOriginalClass().getSimpleName());
            }
        }
        return modelDao;
    }

    @Override
    public void afterDelete(M model, Record record, IUser _usr) throws WishException {

    }

    @Override
    public void afterInsert(M model, Record record, IUser _usr) throws WishException {

    }

    @Override
    public void afterSave(M model, Record record, IUser _usr) throws WishException {

    }

    @Override
    public void afterUpdate(M model, Record record, IUser _usr) throws WishException {

    }

    @Override
    public boolean delete(M model, Record record, IUser _usr) throws WishException {
        if (_usr == null) throw new WishException(WishExceptionKey.MODEL_OPERATE_ERROR);
        preDelete(model, record, _usr);
        if (!model.delete()) return false;
        afterDelete(model, record, _usr);
        return true;
    }

    @Override
    public boolean deleteBatch(List<M> modelList, Record record, IUser _usr) throws WishException {
        for (M model : modelList) delete(model, record, _usr);
        return true;
    }

    @Override
    public boolean deleteWhere(String where, Object... paras) throws WishException {
        String sql = "delete from " + getTableName(getModelClazz()) + " where " + where;
        Db.delete(sql, paras);
        return true;
    }

    @Override
    public M insert(M model, Record record, IUser _usr) throws WishException {
        if (_usr == null) throw new WishException(WishExceptionKey.MODEL_OPERATE_ERROR);
        try {
            preInsert(model, record, _usr);
            Class<? extends Model> clazz = model.getClass();
            String primaryKey = TableMapping.me().getTable(clazz).getPrimaryKey()[0];
            String pk = model.getStr(primaryKey);
            // 主键为空,生成主键id
            if (pk == null || "".equals(pk)) model.set(primaryKey, StrKit.getRandomUUID());
            model.setOrPut("createTime", new Date()).setOrPut("createPerson", _usr.getUserId()).save();
            afterInsert(model, record, _usr);
            afterSave(model, record, _usr);
            return model;
        } catch (Exception e) {
            logger.error("insert异常:  " + e.getMessage(), e);
            if (e instanceof WishException) throw e;
            throw new WishException(WishExceptionKey.MODEL_INSERT_ERROR, e);
        }
    }

    @Override
    public boolean insertBatch(List<M> modelList, Record record, IUser _usr) throws WishException {
        for (M model : modelList) {
            this.insert(model, record, _usr);
        }
        return true;
    }

    @Override
    public void preDelete(M model, Record record, IUser _usr) {

    }

    @Override
    public void preInsert(M model, Record record, IUser _usr) throws WishException {
        Class<?> originalClass = getOriginalClass();
        //获取唯一列注解信息
        if (originalClass.isAnnotationPresent(UniqueColumn.class)) {
            UniqueColumn uniqueColumn = originalClass.getAnnotation(UniqueColumn.class);
            String column=uniqueColumn.column();
            if (StrKit.isBlank(column))
                throw new WishException(WishExceptionKey.MODEL_UNIQUE_COLUMN_CHECK_COLUMN_NOT_EMPTY);
            String[] columnArray=column.split("\\|");
            String[] errorKeyArray=uniqueColumn.errorKeyArray();
            for(int i=0;i<columnArray.length;i++){
                String errorKey=i<errorKeyArray.length?errorKeyArray[i]:null;
                uniqueColumnCheck(columnArray[i], errorKey, model);
            }
        }
    }

    @Override
    public void preUpdate(M model, Record record, IUser _usr) {

    }

    @Override
    public List<M> where(String where, Object... paras) throws WishException {
        String sql = "select * from " + getTableName(getModelClazz()) + " where " + where;
        return getModelDao().find(sql, paras);
    }

    @Override
    public M whereFirst(String where, Object... paras) throws WishException {
        List<M> modelList = where(where, paras);
        return BlankKit.isBlankList(modelList) ? null : modelList.get(0);
    }

    @Override
    public void uniqueColumnCheck(String column, String errorKey, M model) throws WishException {
        String[] columnArray = ArrayUtil.filter(column.split(","), (Editor<String>) t -> (StrKit.notBlank(t)) ? t : null);
        if (columnArray.length == 0)
            throw new WishException(WishExceptionKey.MODEL_UNIQUE_COLUMN_CHECK_COLUMN_NOT_EMPTY);
        StringBuilder sql = new StringBuilder();
        Object[] paramArray = new String[columnArray.length];
        for (int i = 0; i < columnArray.length; i++) {
            if (i > 0) sql.append(" and ");
            String col = columnArray[i];
            sql.append(col).append(" = ?");
            paramArray[i] = model.get(col);
        }
        List<M> modelList = where(sql.toString(), paramArray);
        if (BlankKit.notBlankList(modelList)) {
            if (StrKit.notBlank(errorKey)) {
                throw new WishException(errorKey,paramArray);
            } else {
                throw new WishException(WishExceptionKey.MODEL_UNIQUE_COLUMN_CHECK_ALREADY_EXISTS, ArrayUtil.join(columnArray, ","), ArrayUtil.join(paramArray, ","));
            }
        }
    }

    @Override
    public M update(M model, Record record, IUser _usr) throws WishException {
        if (_usr == null) throw new WishException(WishExceptionKey.MODEL_OPERATE_ERROR);
        try {
            preUpdate(model, record, _usr);
            model.setOrPut("modifyTime", new Date()).setOrPut("modifyPerson", _usr.getUserId()).update();
            afterUpdate(model, record, _usr);
            afterSave(model, record, _usr);
            return model;
        } catch (Exception e) {
            logger.error("update异常:  " + e.getMessage(), e);
            if (e instanceof WishException) throw e;
            throw new WishException(WishExceptionKey.MODEL_UPDATE_ERROR, e);
        }
    }

    @Override
    public boolean updateBatch(List<M> modelList, Record record, IUser _usr) throws WishException {
        for (M model : modelList) {
            this.update(model, record, _usr);
        }
        return true;
    }

    @Override
    public List<M> findList(Kv kv) throws WishException {
        return findList(getSqlKey(), kv);
    }

    @Override
    public List<M> findList(String sqlKey, Kv kv) throws WishException {
        List<M> modelList = getModelDao().template(sqlKey, kv).find();
        if (BlankKit.isBlankList(modelList)) return modelList;
        modelList.forEach((m) -> setModelExtend(m, kv));
        return modelList;
    }

    @Override
    public M findById(String id, Kv kv) throws WishException {
        return findById(getSqlKey(), id, kv);
    }

    @Override
    public M findById(String sqlKey, String id, Kv kv) throws WishException {
        if (StrKit.isBlank(id)) throw new WishException(WishExceptionKey.MODEL_ID_IS_NULL_ERROR);
        String[] primaryKeyArr = TableMapping.me().getTable(getModelClazz()).getPrimaryKey();
        if (primaryKeyArr.length == 1) {
            kv.set(primaryKeyArr[0], id);
        } else if (primaryKeyArr.length > 1) {
            //当表为多主键时 我们需要对id做拆分
            String[] idArr = id.split(",");
            if (primaryKeyArr.length != idArr.length)
                throw new WishException(WishExceptionKey.MODEL_ID_INCONSISTENT_NUMBER_ERROR);
            for (int i = 0; i < primaryKeyArr.length; i++) {
                kv.set(primaryKeyArr[i], idArr[i]);
            }
        }
        M model = getModelDao().template(sqlKey, kv).findFirst();
        setModelExtend(model, kv);
        return model;
    }

    @Override
    public Page<M> findPage(Kv kv) throws WishException {
        return findPage(getSqlKey(), kv);
    }

    @Override
    public Page<M> findPage(String sqlKey, Kv kv) throws WishException {
        int pageNum = Integer.parseInt(kv.getOrDefault("page", 1).toString());
        int limit = Integer.parseInt(kv.getOrDefault("limit", 10).toString());
        SqlKit.orderHandle(kv);
        Page<M> page = getModelDao().template(sqlKey, kv).paginate(pageNum, limit);
        List<M> modelList = page.getList();
        if (BlankKit.isBlankList(modelList)) return page;

        modelList.forEach(m -> setModelExtend(m, kv));
        return page;
    }

    @Override
    public M newModel(Record record, IUser _usr) throws Exception {
        return modelClazz.newInstance();
    }

    /**
     * 获取数据表的名称
     *
     * @param modelCls model class name
     */
    public String getTableName(Class<? extends Model> modelCls) {
        return TableMapping.me().getTable(modelCls).getName();
    }

    /**
     * 设置扩展
     *
     * @param model model object
     * @param kv    查询参数
     */
    public void setModelExtend(Model model, Kv kv) {
        //设置显示列与隐藏列
        setShowOfHideColumn(model, kv);
    }

    /**
     * 设置是否显示隐藏列
     *
     * @param model model
     * @param kv    查询参数
     */
    private void setShowOfHideColumn(Model<?> model, Kv kv) {
        //如果设置了显示列 则后续的隐藏列不用再管 显示列优先于设置隐藏
        String showColumns = kv.getStr("showColumns");
        if (StrKit.notBlank(showColumns)) {
            ModelKit.me().showColumnsForModel(model, showColumns);
            return;
        }
        String hideColumns = kv.getStr("hideColumns");
        if (StrKit.notBlank(hideColumns)) {
            ModelKit.me().hideColumnsForModel(model, hideColumns);
            return;
        }
        //如果参数中未配置显示，隐藏列 则查看代码中是否有配置
        showColumns = getShowColumns();
        if (StrKit.notBlank(showColumns)) {
            ModelKit.me().showColumnsForModel(model, showColumns);
            return;
        }
        hideColumns = getHideColumns();
        if (StrKit.notBlank(hideColumns)) {
            ModelKit.me().hideColumnsForModel(model, hideColumns);
        }
    }

    @Override
    public void downloadExcel(HttpServletResponse response, Kv kv) throws WishException {
        //导出excel的名称 如果页面有传输过来则使用页面的 否则使用表名
        String downloadExcelName = kv.getStr("excelName");
        if (StrKit.isBlank(downloadExcelName)) {
            downloadExcelName = TableMapping.me().getTable(getModelClazz()).getName();
        }
        JSONArray jsonArray = kv.getAs("excelDataArr");
        Map excelHeadMap;
        if (BlankKit.notBlankList(jsonArray)) {
            excelHeadMap = new LinkedHashMap();
            for (int i = 0; i < jsonArray.size(); i++) {
                JSONObject obj = jsonArray.getJSONObject(i);
                excelHeadMap.put(obj.getString("key"), obj.getString("title"));
            }
            kv.remove("excelName");
            kv.remove("excelData");
        } else {
            excelHeadMap = getExcelHeadMap();
        }
        EasyExcelSupport easyExcelSupport = new EasyExcelSupport(response, downloadExcelName).setModelList(findList(kv), excelHeadMap);
        easyExcelSupport.addWriteHandler(new CustomWidthStyleStrategy()).addWriteHandler(new SimpleRowHeightStyleStrategy(new Short("40"), new Short("30")));
        easyExcelSupport.download();
    }

    @Override
    public Map getExcelHeadMap() {
        return null;
    }
}
